package com.means.polymerize.assembly.config;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.means.polymerize.assembly.filter.CacheRequestBodyFilter;
import com.means.polymerize.assembly.filter.XssFilter;
import com.means.polymerize.common.constants.WebFilterOrderEnum;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.util.PathMatcher;
import org.springframework.util.unit.DataSize;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import java.util.Collections;

/**
 * 系统通用核心配置
 *
 * @author xuahifeng
 * @date 2021.04.24
 */
@Configuration
@EnableWebSocket
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class CoreConfig {
    private static final String ASYNCHRONOUS_CHANNEL_PROVIDER = "java.nio.channels.spi.AsynchronousChannelProvider";
    private static final String ENHANCE_ASYNCHRONOUS_CHANNEL_PROVIDER = "org.smartboot.aio.EnhanceAsynchronousChannelProvider";

    /**
     * 配置属性
     */
    @Bean
    public void propertyConfig() {
        System.setProperty(ASYNCHRONOUS_CHANNEL_PROVIDER, ENHANCE_ASYNCHRONOUS_CHANNEL_PROVIDER);
    }

    /**
     * 文件上传配置
     *
     * @return factory multipart config element
     */
    @Bean
    public MultipartConfigElement multipartConfigElement() {
        final MultipartConfigFactory factory = new MultipartConfigFactory();
        /* 文件最大 */
        factory.setMaxFileSize(DataSize.ofMegabytes(200L));
        /* 设置总上传数据总大小 */
        factory.setMaxRequestSize(DataSize.ofMegabytes(500L));
        return factory.createMultipartConfig();
    }

    /**
     * 配置spring boot消息解析器
     * Fast json http message converters http message converters.
     *
     * @return the http message converters
     */
    @Bean
    public HttpMessageConverters fastJsonHttpMessageConverters() {
        final FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        final FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        fastConverter.setFastJsonConfig(fastJsonConfig);
        return new HttpMessageConverters(fastConverter);
    }

    /**
     * 设置过滤器优先级
     *
     * @param filter filter
     * @param order  order
     * @return FilterRegistrationBean
     */
    private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
        final FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
        bean.setOrder(order);
        return bean;
    }

    /**
     * 核心过滤器
     *
     * @return the cors filter
     */
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig());
        return createFilterBean(new CorsFilter(source), WebFilterOrderEnum.CORS_FILTER);
    }

    /**
     * mybatis Plus拦截器
     *
     * @return the mybatis plus interceptor
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        final MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    /**
     * 跨域配置
     *
     * @return CorsConfiguration
     */
    private CorsConfiguration buildConfig() {
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addExposedHeader("*");
        corsConfiguration.addAllowedOriginPattern("*");
        /* 允许任何域名使用 */
        corsConfiguration.addAllowedOrigin("*");
        /* 允许任何头 */
        corsConfiguration.addAllowedHeader("*");
        /* 允许任何方法 */
        corsConfiguration.addAllowedMethod("*");
        return corsConfiguration;
    }

    /**
     * 创建 XssFilter Bean，解决 Xss 安全问题
     *
     * @param pathMatcher the path matcher
     * @return the filter registration bean
     */
    @Bean
    public FilterRegistrationBean<XssFilter> xssFilter(PathMatcher pathMatcher) {
        return createFilterBean(new XssFilter(XssFilter.XssProperties.builder()
                .enable(true)
                .excludeUrls(Collections.emptyList())
                .build(), pathMatcher), WebFilterOrderEnum.XSS_FILTER);
    }

    /**
     * 创建 RequestBodyCacheFilter Bean，可重复读取请求内容
     *
     * @return the filter registration bean
     */
    @Bean
    public FilterRegistrationBean<CacheRequestBodyFilter> requestBodyCacheFilter() {
        return createFilterBean(new CacheRequestBodyFilter(), WebFilterOrderEnum.REQUEST_BODY_CACHE_FILTER);
    }

    /**
     * 启用WebSocket
     *
     * @return the server endpoint exporter
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    /**
     * redis监听容器
     *
     * @param connectionFactory connectionFactory
     * @param listenerAdapter   listenerAdapter
     * @return RedisMessageListenerContainer
     */
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
        final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic(""));
        return container;
    }

}
